#pragma once

#include "bezier_guide.h"

namespace outline {

/// The class accumulates information about sub-contour,
/// and provides summed square of discrepancy of intermediate points from best-fitting bezier.
/// The class differs from incremental_bezier_guide<T,R> only by that it stores contour to be matched inside
template <typename T, typename R = typename type_traits<T>::real_type>
class bezier_estimator {
public:
	typedef point<T>	sample;
	typedef R			real_type;
	typedef point<R>	real_point_type;
	typedef bezier<R>	bezier_type;
	struct patch { ///< bezier patch
		real_point_type d1, d2; ///< tangents at the start node and at the end nodes
	};
public:
	bezier_estimator() 
		{ init(); }
	bezier_estimator(sample const & s) {
		init();
		start(s); 
	}
	static int min_step_len()
		{ return 4; }
public: // parameters
	void set_params(uint max_iterations, R stop_mean_discrepancy, R oversample_factor) {
		guide_.max_iterations = max_iterations;
		guide_.stop_mean_discrepancy = stop_mean_discrepancy;
		guide_.oversample_factor = oversample_factor;
	}
	void get_params(uint & max_iterations, R & stop_mean_discrepancy, R & oversample_factor) {
		max_iterations = guide_.max_iterations;
		stop_mean_discrepancy = guide_.stop_mean_discrepancy;
		oversample_factor = guide_.oversample_factor;
	}
public: //methods' description see in fixed_line_estimator< point<T> >
	void start(sample const & s) {
		discrepancy_ = 0;
		guide_.clear(); 
		contour_.clear(); 
		contour_.push_back(s); 
	}
	void add_sample(sample const & s)
		{ contour_.push_back(s); }
	double add_sample_and_estimate(sample const & s, patch * p = 0, bool swap_ends = false) {
		contour_.push_back(s);
		discrepancy_ = guide_.fit(contour_, 0, contour_.size());
		if (p) {
			p->d1 = guide_.b.start_d();
			p->d2 = guide_.b.finish_d();
			if (swap_ends)
				std::swap(p->d1, p->d2);
		}
		return discrepancy_;
	}
	double add_start_and_estimate(sample const & s, patch * p = 0, bool swap_ends = false) 
		{ return add_sample_and_estimate(s, p, swap_ends); }
	double min_discrepancy() const
		//{ return 0; } ///< I don't know how to estimate it
		{ return 0.9 * discrepancy_; } ///< this formula does not follow from anywhere
private:
	void init() {
		set_params(1, 0.01, 1);
		discrepancy_ = 0;
	}
private:
	typedef std::vector<sample> outline_type;
private:
	incremental_bezier_guide<T, R> guide_;
	outline_type contour_;
	double discrepancy_;
};

} //namespace outline
